package com.zrc.japktool.util;

import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.FileHeader;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.model.enums.CompressionLevel;
import net.lingala.zip4j.model.enums.CompressionMethod;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;

public class Zip4jUtil {

    /**
     * 通过zip4j对文件进行压缩
     *
     * @param files  压缩的文件列表
     * @param outZip 输出的zip文件
     * @throws ZipException 异常
     */
    public static void zip(File[] files, File outZip) {
        if (files == null || files.length <= 0) {
            return;
        }

        try (ZipFile zipFile = new ZipFile(outZip)) {
            ZipParameters parameters = new ZipParameters();
            parameters.setCompressionMethod(CompressionMethod.STORE);
            parameters.setCompressionLevel(CompressionLevel.NORMAL);

            for (File f : files) {
                if (f.isDirectory()) {
                    zipFile.addFolder(f, parameters);
                } else {
                    zipFile.addFile(f, parameters);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void zip(String sourceFolderPath, String outputZipPath){
        File sourceFolder = new File(sourceFolderPath);
        if (!sourceFolder.exists()) {
            return;
        }

        // 检查文件夹是否为空（无文件和子文件夹）
        File[] files = sourceFolder.listFiles();
        if (files == null || files.length == 0) {
            throw new IllegalArgumentException("Source folder is empty: " + sourceFolderPath);
        }

        try (ZipFile zipFile = new ZipFile(outputZipPath)) {
            if (sourceFolder.isDirectory()) {
                zipFile.addFolder(sourceFolder);
            } else {
                zipFile.addFile(sourceFolder);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 采用zip4j解压
     *
     * @param inputFile 输入的zip文件对象
     * @param outFile   解压输出的目录
     */
    public static void unzip(String inputFile, String outFile) {
        try( ZipFile zipFile = new ZipFile(new File(inputFile))) {
            zipFile.extractAll(new File(outFile).getAbsolutePath());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 提取单个文件到指定的目录中
     *
     * @param zipPath    zip源文件路径
     * @param fileName   zip种文件的名称(xxx.txt,xxx/mmm/xxx.txt)
     * @param outDirPath 外部输出的目录路径
     */
    public static void extractFile(String zipPath, String fileName, String outDirPath) {
        zipPath = zipPath.replace("\\", "/");
        fileName = fileName.replace("\\", "/");
        outDirPath = outDirPath.replace("\\", "/");
        try(ZipFile zipFile = new ZipFile(zipPath)) {
            zipFile.extractFile(fileName, outDirPath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 提取 ZIP 中的指定文件夹到外部目录（不保留原ZIP中的父目录结构）
     *
     * @param zipPath    ZIP 文件路径
     * @param zipDir     ZIP 中的文件夹路径（如 "bb/sfsf/"）
     * @param outDirPath 解压到的目标目录（如 "C:/test/bb"）
     */
    public static void extractDir(String zipPath, String zipDir, String outDirPath) {
        zipPath = zipPath.replace("\\", "/");
        zipDir = zipDir.replace("\\", "/");
        outDirPath = outDirPath.replace("\\", "/");
        try(ZipFile zipFile = new ZipFile(zipPath)) {
            if (!zipDir.endsWith("/") && !zipDir.isEmpty()) {
                zipDir += "/";
            }
            List<FileHeader> fileHeaders = zipFile.getFileHeaders();
            for (FileHeader fileHeader : fileHeaders) {
                if (fileHeader.isDirectory()){
                    continue;
                }
                String filePathInZip = fileHeader.getFileName();
                // 处理目录条目
                if (filePathInZip.endsWith("/")) {
                    if (filePathInZip.startsWith(zipDir)) {
                        String relativeDir = filePathInZip.substring(zipDir.length());
                        new File(outDirPath, relativeDir).mkdirs();
                    }
                    continue;
                }

                // 处理文件条目
                if (filePathInZip.startsWith(zipDir)) {
                    zipFile.extractFile(fileHeader, outDirPath);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 采用Zip4j进行添加dex,不进行压缩
     * 向zip中追加一个文件
     *
     * @param zipPath  zip包
     * @param filePath 追加的文件
     */
    public static void addFile(String zipPath, String filePath, String rootFolder) {
        zipPath = zipPath.replace("\\", "/");
        filePath = filePath.replace("\\", "/");
        rootFolder = rootFolder.replace("\\", "/");
        try (ZipFile zipFile = new ZipFile(zipPath)){
            ZipParameters parameters = new ZipParameters();
            parameters.setCompressionMethod(CompressionMethod.DEFLATE);
            parameters.setCompressionLevel(CompressionLevel.NORMAL);
            if (rootFolder == null) {
                rootFolder = "";
            }
            parameters.setRootFolderNameInZip(rootFolder);
            zipFile.addFile(filePath, parameters);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 将指定目录的所有文件添加到 ZIP 文件的指定目录中
     *
     * @param zipPath  目标 ZIP 文件路径
     * @param dirPath  源目录路径（要压缩的目录）
     * @param zipRoute ZIP 文件中的目标目录（如 "data/"）
     */
    public static void addDir(String zipPath, String dirPath, String zipRoute) {
        zipPath = zipPath.replace("\\", "/");
        dirPath = dirPath.replace("\\", "/");
        zipRoute = zipRoute.replace("\\", "/");
        try {
            File sourceDir = new File(dirPath);
            if (!sourceDir.exists() || !sourceDir.isDirectory()) {
                throw new IllegalArgumentException("Source directory does not exist or is not a directory!");
            }

            if (!zipRoute.endsWith("/")) {
                zipRoute += "/";
            }

            try (ZipFile zipFile = new ZipFile(zipPath)) {
                ZipParameters parameters = new ZipParameters();
                parameters.setCompressionMethod(CompressionMethod.DEFLATE);
                parameters.setCompressionLevel(CompressionLevel.NORMAL);

                String finalTargetZipDir = zipRoute;
                try (Stream<Path> pathStream = Files.walk(Paths.get(dirPath))) {
                    pathStream.filter(Files::isRegularFile)
                            .forEach(file -> {
                                String relativePath = sourceDir.toPath().relativize(file)
                                        .toString()
                                        .replace("\\", "/");
                                String entryName = finalTargetZipDir + relativePath;
                                parameters.setFileNameInZip(entryName);
                                try {
                                    zipFile.addFile(file.toFile(), parameters);
                                } catch (ZipException e) {
                                    throw new RuntimeException(e);
                                }
                            });
                } catch (IOException e) {
                    e.printStackTrace();
                }
//                Files.walk(Paths.get(dirPath))
//                        .filter(Files::isRegularFile)
//                        .forEach(file -> {
//                            String relativePath = sourceDir.toPath().relativize(file)
//                                    .toString()
//                                    .replace("\\", "/");
//                            String entryName = finalTargetZipDir + relativePath;
//                            parameters.setFileNameInZip(entryName);
//                            try {
//                                zipFile.addFile(file.toFile(), parameters);
//                            } catch (ZipException e) {
//                                throw new RuntimeException(e);
//                            }
//                        });
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除目录
     *
     * @param zipPath zip对象
     * @param dirPath 文件路径
     */
    public static void delDir(String zipPath, String dirPath) {
        zipPath = zipPath.replace("\\", "/");
        dirPath = dirPath.replace("\\", "/");
        try ( ZipFile zipFile = new ZipFile(zipPath)){
            List<FileHeader> files = zipFile.getFileHeaders();
            List<String> dexFiles = new ArrayList<>();
            for (FileHeader file : files) {
                if (file.getFileName().contains(dirPath)) {
                    dexFiles.add(file.getFileName());
                }
            }
            zipFile.removeFiles(dexFiles);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 删除文件
     *
     * @param zipPath  zip对象
     * @param filePath 文件路径
     */
    public static void delFile(String zipPath, String filePath) {
        zipPath = zipPath.replace("\\", "/");
        filePath = filePath.replace("\\", "/");
        try (ZipFile zipFile = new ZipFile(zipPath)) {
            zipFile.removeFile(filePath);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 从 ZIP 文件中读取指定文件的字符串内容
     *
     * @param zipPath   ZIP 文件路径
     * @param fileInZip ZIP 中要读取的文件路径
     * @return 文件内容字符串
     * @throws ZipException             如果 ZIP 文件操作出错
     * @throws IOException              如果文件读取出错
     * @throws IllegalArgumentException 如果文件不存在于 ZIP 中
     */
    public static String readStr(String zipPath, String fileInZip) {
        zipPath = zipPath.replace("\\", "/");
        fileInZip = fileInZip.replace("\\", "/");
        try (ZipFile zipFile = new ZipFile(zipPath)) {
            if (zipFile.isEncrypted()) {
                throw new ZipException("ZIP 文件已加密，需要密码才能解压");
            }
            FileHeader fileHeader = zipFile.getFileHeader(fileInZip);
            if (fileHeader == null) {
                throw new IllegalArgumentException("文件 '" + fileInZip + "' 不在 ZIP 包中");
            }
            try (InputStream inputStream = zipFile.getInputStream(fileHeader); ByteArrayOutputStream result = new ByteArrayOutputStream()) {
                byte[] buffer = new byte[1024];
                int length;
                while ((length = inputStream.read(buffer)) != -1) {
                    result.write(buffer, 0, length);
                }
                return result.toString(StandardCharsets.UTF_8);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 从 ZIP 文件中读取指定文件的 byte[]
     *
     * @param zipPath       ZIP 文件路径
     * @param filePathInZip ZIP 文件内的文件路径（如 "data/test.txt"）
     * @return 文件的 byte[] 数据，如果文件不存在则返回 null
     */
    public static byte[] readFileBytes(String zipPath, String filePathInZip) {
        // 先获取 FileHeader，避免在 try 括号内做复杂操作
        FileHeader fileHeader = null;
        try (ZipFile zipFile = new ZipFile(zipPath)) {
            fileHeader = zipFile.getFileHeader(filePathInZip);
            if (fileHeader == null) {
                throw new IllegalArgumentException("File not found in ZIP: " + filePathInZip);
            }
            // 显式将 InputStream 声明为资源
            try (InputStream is = zipFile.getInputStream(fileHeader)) {
                return is.readAllBytes();
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 向 ZIP 文件写入 byte[] 数据（覆盖已有文件）
     */
    public static void writeFileBytes(String zipPath, String filePathInZip, byte[] data, boolean isStore){
        try (ZipFile zipFile = new ZipFile(zipPath); ByteArrayInputStream is = new ByteArrayInputStream(data)) {
            ZipParameters parameters = new ZipParameters();
            parameters.setFileNameInZip(filePathInZip);
            if (isStore){
                parameters.setCompressionMethod(CompressionMethod.STORE);
            }else {
                parameters.setCompressionMethod(CompressionMethod.DEFLATE);
                parameters.setCompressionLevel(CompressionLevel.NORMAL);
            }
            if (zipFile.getFileHeader(filePathInZip) != null) {
                zipFile.removeFile(filePathInZip);
            }
            zipFile.addStream(is, parameters);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 将指定文件夹下的所有内容（包括嵌套文件夹）打包成ZIP文件
     *
     * @param sourceFolderPath 源文件夹路径
     * @param targetZipPath    目标ZIP文件路径
     * @param includeRootDir   是否在ZIP中包含源文件夹本身作为根目录
     * @throws IOException 当压缩过程中出现错误时抛出
     */
    public static void compressFolderWithNested(String sourceFolderPath, String targetZipPath, boolean includeRootDir) throws IOException {

        // 验证源文件夹
        File sourceFolder = new File(sourceFolderPath);
        validateSourceFolder(sourceFolder);

        // 准备目标文件
        prepareTargetFile(targetZipPath);

        // 配置压缩参数
        ZipParameters zipParams = new ZipParameters();
        zipParams.setCompressionMethod(CompressionMethod.STORE); // 标准压缩方式
        zipParams.setCompressionLevel(CompressionLevel.NORMAL);    // 平衡压缩率和速度
        zipParams.setIncludeRootFolder(includeRootDir);            // 控制是否包含根文件夹

        // 执行压缩
        try (ZipFile zipFile = new ZipFile(targetZipPath)) {
            zipFile.addFolder(sourceFolder, zipParams);
        } catch (ZipException e) {
            throw new IOException("压缩失败: " + e.getMessage(), e);
        }
    }

    /**
     * 验证源文件夹是否有效
     */
    private static void validateSourceFolder(File sourceFolder) throws IOException {
        if (!sourceFolder.exists()) {
            throw new IOException("源文件夹不存在: " + sourceFolder.getAbsolutePath());
        }
        if (!sourceFolder.isDirectory()) {
            throw new IOException("指定路径不是文件夹: " + sourceFolder.getAbsolutePath());
        }
        File[] contents = sourceFolder.listFiles();
        if (contents == null || contents.length == 0) {
            throw new IOException("源文件夹为空，无法压缩: " + sourceFolder.getAbsolutePath());
        }
        // 检查是否有可访问的内容
        if (!hasAccessibleContent(sourceFolder)) {
            throw new IOException("源文件夹内容不可访问（可能是权限问题）: " + sourceFolder.getAbsolutePath());
        }
    }

    /**
     * 检查文件夹是否有可访问的内容
     */
    private static boolean hasAccessibleContent(File folder) {
        File[] files = folder.listFiles();
        if (files == null) return false; // 无权限访问该文件夹

        for (File file : files) {
            if (file.isDirectory()) {
                if (hasAccessibleContent(file)) {
                    return true;
                }
            } else {
                if (file.canRead()) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 准备目标文件（创建父目录、删除已有文件）
     */
    private static void prepareTargetFile(String targetZipPath) {
        File targetFile = new File(targetZipPath);
        // 创建父目录
        File parentDir = targetFile.getParentFile();
        if (parentDir != null && !parentDir.exists()) {
            parentDir.mkdirs();
        }
        // 删除已存在的目标文件
        if (targetFile.exists() && !targetFile.delete()) {
            System.out.println("警告: 已存在同名ZIP文件并尝试删除失败，可能导致压缩异常");
        }
    }

    /**
     * 获取ZIP文件中所有文件夹的路径（不含文件），路径格式调整为：
     * - 移除末尾的"/"
     * - 中间的"/"替换为"."
     *
     * @param zipPath ZIP文件路径
     * @return 格式化后的文件夹路径列表
     * @throws Exception 当ZIP文件操作失败时抛出
     */
    public static List<String> getAllFolderPaths(String zipPath) throws Exception {
        List<String> folderPaths = new ArrayList<>();
        // 统一路径分隔符
        zipPath = zipPath.replace("\\", "/");

        try (ZipFile zipFile = new ZipFile(zipPath)) {
            // 获取所有条目头信息
            List<FileHeader> fileHeaders = zipFile.getFileHeaders();
            if (fileHeaders == null || fileHeaders.isEmpty()) {
                return folderPaths;
            }

            // 用于存储文件夹路径（去重）
            Set<String> folderSet = new HashSet<>();

            for (FileHeader header : fileHeaders) {
                String entryName = header.getFileName().replace("\\", "/");
                String folderPath = null;

                // 情况1：条目本身是文件夹（以"/"结尾）
                if (entryName.endsWith("/")) {
                    // 移除末尾的"/"，再替换中间的"/"为"."
                    folderPath = entryName.substring(0, entryName.length() - 1).replace("/", ".");
                }
                // 情况2：条目是文件，提取其所在的文件夹路径
                else {
                    int lastSlashIndex = entryName.lastIndexOf("/");
                    if (lastSlashIndex > 0) {
                        // 提取文件所在文件夹（如"a/b/c.txt" → "a/b"）
                        folderPath = entryName.substring(0, lastSlashIndex).replace("/", ".");
                    } else if (lastSlashIndex == 0) {
                        // 根目录下的文件（如"/file.txt"），根目录表示为空字符串
                        folderPath = "";
                    }
                }

                // 添加非空路径（避免空字符串重复添加）
                if (folderPath != null && !folderPath.isEmpty()) {
                    folderSet.add(folderPath);
                } else if (folderPath != null) {
                    // 根目录单独处理，只添加一次
                    folderSet.add("");
                }
            }

            // 转换为列表并排序（保证输出顺序一致）
            folderPaths.addAll(folderSet);
            folderPaths.sort(String::compareTo);
        }

        return folderPaths;
    }

    public static void main(String[] args) throws Exception {
        //String workPath = "C:/Users/Administrator/Desktop/test/";
        //String zipPath = "C:/Users/Administrator/Desktop/test/demo.xapk";
        //delFile(zipPath,"resources.arsc");                                                        //删除单个文件
        //delDir(zipPath,"res/color");                                                              //删除目录
        //addFile(zipPath,"C:/Users/Administrator/Desktop/test/mini222.jks","xxx/ccc");             //添加单个文件
        //addDir(zipPath,"C:/Users/Administrator/Desktop/test/vsd","bb/");                          //添加目录
        //extractFile(zipPath, "","classes.dex", "C:/Users/Administrator/Desktop/test/");           //添加目录
        //extractDir(zipPath, "bb/sfsf/", "C:/Users/Administrator/Desktop/test");                   //提取目录
        //System.out.println(readStr(zipPath, "manifest.json"));                                    //读取xxx文件中的字符串

        for (String allFolderPath : getAllFolderPaths("C:\\Users\\15666\\.m2\\repository\\org\\fxmisc\\wellbehaved\\wellbehavedfx\\0.3.3\\wellbehavedfx-0.3.3.jar")) {
            System.out.println("exports " + allFolderPath + ";");
        }
    }
}